﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using SadlandVillage.Minigames.Restaurant.Helper;
using SadlandVillage.Core.Helper;
using System.Timers;
using SadlandVillage.Core;

namespace SadlandVillage.Minigames.Restaurant
{
    public enum NanoGameState
    {
        Description,
        CountDown,
        Running
    }

    public class Dish : Microsoft.Xna.Framework.DrawableGameComponent
    {
        //Events and Delegates
        public delegate void PlateLoserEventHandle(Plates nextPlate);
        public event PlateLoserEventHandle PlateLoser;

        //Game dynamics attributes
        private NanoGameState gameState;
        private int lastTickCount = 0;
        private Color backColor = Color.Snow;

        //Drawing attributes
        private Texture2D dishEmpty;
        private Texture2D dishFull;
        private Texture2D blackPoint;
        private ContourPoint[,] dishContour;
        private SpriteBatch sBatch;

        //Dish information attributes
        private Plates nextPlate;
        private Plates thisPlate;
        private int taskTime;

        //Countdown attributes
        private Timer timer;
        private int countDown = 1;
        private int descriptionCounter = 0;

        //Points calculation attributes
        int dishContourPoints = 0;
        int dishMatchedPoints = 0;

        //Aux
        private Vector2 position = new Vector2();
        private Vector2 oldPosition; 

        public Dish(Texture2D emptyTexture, Texture2D fullTexture, int timeToComplete, Plates thisPlate, Plates nextPlate, SpriteBatch sBatch)
            : base(GlobalContainer.game)
        {
            this.dishEmpty = emptyTexture;
            this.dishFull = fullTexture;
            this.nextPlate = nextPlate;
            this.taskTime = timeToComplete;
            this.thisPlate = thisPlate;
            this.sBatch = sBatch;

            dishContour = GetContour(dishEmpty, ref dishContourPoints);

            blackPoint = StaticContainer.blackPoint;
            StaticContainer.platesXtotal.Add(thisPlate, new Pair<int>(dishContourPoints, 0));

            lastTickCount = 0;

            gameState = NanoGameState.Description;

            timer = new Timer(1000);
            timer.Elapsed +=new ElapsedEventHandler(OnTimeElapsed);

            oldPosition= new Vector2(-500, -500);
        }

        protected void OnTimeElapsed(object sender, EventArgs e)
        {
            if (this.countDown > 0)
            {
                this.countDown--;
            }
            else
            {
                timer.Stop();
            }
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here

            base.Initialize();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            KeyboardState keyState = Keyboard.GetState();

            if (keyState.IsKeyDown(Keys.Escape))
            {
                PlateLoser(Plates.None);
            }

            switch(gameState){
                case NanoGameState.Description:
                    descriptionCounter++;
                    if (descriptionCounter == 200)
                    {
                        gameState = NanoGameState.CountDown;
                        timer.Start();
                        descriptionCounter = 0;
                    }

                    break;
                case NanoGameState.CountDown:
                    if (countDown == 0)
                    {
                        gameState = NanoGameState.Running;
                        lastTickCount = System.Environment.TickCount;
                    }
                    break;
                case NanoGameState.Running:
                    MouseState state = Mouse.GetState();
                    Crosshair cursor1 = Input.GetCursor(Input.Cursors.Cursor1);
                    //stop blinking effect
                    if (cursor1.Position.X < -10 || cursor1.Position.Y < -10)
                    {
                        cursor1.Position = oldPosition;
                    }

                    if (Input.WiimoteConnected)
                    {
                        IsInContour(30,(int)cursor1.Position.Y, (int)cursor1.Position.X);                        
                        position.X = (int)cursor1.Position.X;
                        position.Y = (int)cursor1.Position.Y;
                    }
                    else
                    {
                        IsInContour(30, state.Y, state.X);
                        position.X = state.X;
                        position.Y = state.Y;
                    }

                    if (CheckTime())
                    {
                        PlateLoser(nextPlate);
                    }
                    break;
            }
            oldPosition = position;
            base.Update(gameTime);
        }

        public void Dispose(bool disposing)
        {
            if (disposing)
            {
                timer.Dispose();
            }

            base.Dispose();
        }

        public override void Draw(GameTime gameTime)
        {
            SpriteFont font = (SpriteFont)GlobalContainer.services.GetService(typeof(SpriteFont));
            GraphicsDevice graphics = GlobalContainer.graphics;
            graphics.Clear(backColor);

            switch (gameState)
            {
                case NanoGameState.Running:
                    sBatch.Begin();                  

                    for (int i = 0; i < dishEmpty.Height; i++)
                    {
                        for (int j = 0; j < dishEmpty.Width; j++)
                        {
                            if (dishContour[i, j].IsContour && !dishContour[i, j].IsMatched)
                            {
                                sBatch.Draw(blackPoint, new Vector2(j, i), Color.Black);
                            }
                        }
                    }


                    sBatch.DrawString(font, ((dishMatchedPoints * 100) / dishContourPoints).ToString() + "% completed", new Vector2(20, 20), Color.Black);

                    sBatch.DrawString(font, (((taskTime - (System.Environment.TickCount - lastTickCount)) / 1000) + 1).ToString() + " s", new Vector2(600, 20), Color.Black);
                    sBatch.End();

                    sBatch.Begin(SpriteBlendMode.AlphaBlend);
                    float blend = 255 * ((float)dishMatchedPoints / (float)dishContourPoints);
                    Color color = new Color(255, 255, 255, (byte)blend);
                    sBatch.Draw(dishFull, Vector2.Zero, color);
                    sBatch.End();
                    sBatch.Begin();
                    sBatch.Draw(StaticContainer.cursor, position - new Vector2(15, 15), Color.White);
                    sBatch.End();
                    break;
                case NanoGameState.Description:
                    sBatch.Begin();
                    sBatch.Draw(StaticContainer.kitchen, Vector2.Zero, Color.White);
                    string text1 = "Mr. Macarroni wants a " + PlatesHelper.GetPlateName(thisPlate) + "!";
                    string text2 = "You have " + taskTime / 1000 + " seconds to get it ready!";

                    StringEffects.PrintShadowedString(sBatch, font, text1, new Vector2(200, 75), Color.White, Color.Black, 2);
                    StringEffects.PrintShadowedString(sBatch, font, text2, new Vector2(200, 105), Color.White, Color.Black, 2);
                    
                    sBatch.End();
                    break;
                case NanoGameState.CountDown:
                    sBatch.Begin();
                    sBatch.Draw(StaticContainer.kitchenEmpty, Vector2.Zero, Color.White);
                    StringEffects.PrintShadowedString(sBatch, StaticContainer.getReadyFont, "Get ready!", new Vector2(250, 150), Color.Yellow, Color.Black, 2);
                    StringEffects.PrintShadowedString(sBatch, StaticContainer.getReadyFont, countDown.ToString(), new Vector2(400,285), Color.Yellow, Color.Black, 2);
                    sBatch.End();
                    break;
            }

            base.Draw(gameTime);
        }

        #region Helper
        private ContourPoint[,] GetContour(Texture2D countourTexture, ref int contours)
        {
            ContourPoint[,] result = new ContourPoint[countourTexture.Height, countourTexture.Width];
            uint[] array = new uint[countourTexture.Height * countourTexture.Width];
            countourTexture.GetData<uint>(array);

            for (int i = 0; i < countourTexture.Height; i++)
            {
                for (int j = 0; j < countourTexture.Width; j++)
                {
                    result[i, j] = new ContourPoint(array[i * countourTexture.Width + j] != 0);
                    if (result[i, j].IsContour)
                    {
                        contours++;
                    }
                }
            }

            return result;
        }

        private bool IsInContour(uint tolerance, int x, int y)
        {
            bool result = false;

            int dim1 = dishContour.GetLength(0);
            int dim2 = dishContour.GetLength(1);

            if (x >= 0 && x < dim1 && y >= 0 && y < dim2){
                if (dishContour[x, y].IsContour)
                {
                    if (!dishContour[x, y].IsMatched)
                    {
                        dishContour[x, y].IsMatched = true;
                        dishMatchedPoints++;
                    }

                    result = true;
                }
            }

            for (int i = 1; i <= tolerance; i++)
            {
                for (int j = 1; j <= tolerance; j++)
                {
                    if ((x - i) >= 0 && (x - i) < dim1 && (y - j) >= 0 && (y - j) < dim2)
                    {
                        if (dishContour[x - i, y - j].IsContour)
                        {
                            if (!dishContour[x - i, y - j].IsMatched)
                            {
                                dishContour[x - i, y - j].IsMatched = true;
                                dishMatchedPoints++;
                            }

                            result = true;
                        }
                    }
                    if ((x + i) >= 0 && (x + i) < dim1 && (y + j) >= 0 && (y + j) < dim2)
                    {
                        if (dishContour[x + i, y + j].IsContour)
                        {
                            if (!dishContour[x + i, y + j].IsMatched)
                            {
                                dishContour[x + i, y + j].IsMatched = true;
                                dishMatchedPoints++;
                            }

                            result = true;
                        }
                    }
                }
            }

            return result;
        }

        private bool CheckTime()
        {
            bool result = false;
            if ((System.Environment.TickCount - lastTickCount) >= taskTime)
            {
                lastTickCount = System.Environment.TickCount;
                StaticContainer.platesXtotal[thisPlate].tail = dishMatchedPoints;
                result = true;
            }
            return result;
        }
        #endregion
    }
}
